package com.gjy.util.sb.aop.aspect;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gjy.util.sb.aop.annotation.LogRecord;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Method;

/**
 * @author 宫静雨
 * @version 1.0
 * @since 2023-10-23 15:38:35
 */
@Component
@Aspect
@Order(0)
public class LogRecordAspect {

    private static final Logger log = LoggerFactory.getLogger(LogRecordAspect.class);

    @Resource
    private ObjectMapper objectMapper;

    ThreadLocal<Long> startTime = new ThreadLocal<>();

    @Before(value = "@annotation(com.gjy.util.sb.aop.annotation.LogRecord)")
    public void before() {
        startTime.set(System.currentTimeMillis());
    }

    /**
     * 正常返回通知,拦截用户操作日志,连接点正常执行完成后执行
     * 如果连接点抛出异常，则不会执行
     *
     * @param point 切入点
     * @param r     返回结果
     */
    @AfterReturning(value = "@annotation(com.gjy.util.sb.aop.annotation.LogRecord)", returning = "r")
    public void recordLog(JoinPoint point, Object r) throws Exception {
        /* web 环境中获取 request
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = (HttpServletRequest) requestAttributes
                .resolveReference(RequestAttributes.REFERENCE_REQUEST);
        request.getHeader("");*/

        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        if (method.isAnnotationPresent(LogRecord.class)) {
            LogRecord logRecord = method.getAnnotation(LogRecord.class);
            String module = logRecord.module();
            String type = logRecord.type();
            String desc = logRecord.desc();
            log.info("module:{}, type:{}, desc:{}, result:{}", module, type, desc, r);
        }
        String className = point.getTarget().getClass().getName();
        String methodName = method.getName();
        String param = param(point);
        long l = System.currentTimeMillis() - startTime.get();
        log.info("className:{}, methodName:{}, param:{}, useTime:{}", className, methodName, param, l);
        startTime.remove();
        // 可以将相关信息保存到数据库中
    }

    /**
     * 异常返回通知，用于拦截异常日志信息 连接点抛出异常后执行
     *
     * @param point 切入点
     * @param e     异常信息
     */
    //@AfterThrowing(value = "execution(* com.gjy.util..*.*(..))", throwing = "e")
    public void exceptionLog(JoinPoint point, Throwable e) throws Exception {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        if (method.isAnnotationPresent(LogRecord.class)) {
            LogRecord logRecord = method.getAnnotation(LogRecord.class);
            String module = logRecord.module();
            String type = logRecord.type();
            String desc = logRecord.desc();
            log.info("module:{}, type:{}, desc:{}, exception:{}", module, type, desc,
                    e.getClass().getName() + ":" + e.getMessage());
        }
        String className = point.getTarget().getClass().getName();
        String methodName = method.getName();
        String param = param(point);
        log.info("className:{}, methodName:{}, param:{}", className, methodName, param);
        // 可以将收集的信息存进数据库中
    }

    private String param(JoinPoint point) throws JsonProcessingException {
        Object[] args = point.getArgs();
        String[] names = ((MethodSignature) point.getSignature()).getParameterNames();
        if (names.length == 0) {
            return "";
        }
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < names.length; i++) {
            sb.append(names[i]).append(":").append(objectMapper.writeValueAsString(args[i])).append(",");
        }
        return sb.substring(0, sb.length() - 1);
    }

}
